/***************************************************************************
 *   oboe.core.SigSlot                                                     *
 *   Copyright (C) 2011 by Felipe Manga                                    *
 *   lufeboma@gmail.com                                                    *
 *                                                                         *
 *   Permission is hereby granted, free of charge, to any person obtaining *
 *   a copy of this software and associated documentation files (the       *
 *   "Software"), to deal in the Software without restriction, including   *
 *   without limitation the rights to use, copy, modify, merge, publish,   *
 *   distribute, sublicense, and/or sell copies of the Software, and to    *
 *   permit persons to whom the Software is furnished to do so, subject to *
 *   the following conditions:                                             *
 *                                                                         *
 *   The above copyright notice and this permission notice shall be        *
 *   included in all copies or substantial portions of the Software.       *
 *                                                                         *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       *
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    *
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR     *
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
 *   OTHER DEALINGS IN THE SOFTWARE.                                       *
 ***************************************************************************/

package oboe.core
{
	import flash.utils.Dictionary;
	import flash.utils.getQualifiedClassName;
	
	import oboe.controllers.Collection;
	
	public class SigSlot extends SigSlotCollection
	{
		private static var nextSigSlotId:int = 1;
		private static const globalPool:Collection = new Collection().setDebugTraces(false);
		
		private var sigslotParent:SigSlot;		
		private var id:int;
		private var isRooted:Boolean = false;
		private var pipes:Dictionary;
		private var signals:Object;
		private var complete:Boolean = false;


		public function SigSlot()
		{
			this.id = nextSigSlotId++;
			super();
			this.signals = SigSlotCollection.globalSigIndex[ Object(this).constructor ];
			this.isRooted = Oboe.root == this;
		}
		
		protected function getIsRooted():Boolean
		{
			return this.isRooted;
		}
		
		[slot] public function onAddToRoot():void
		{
			globalPool.add( this );
			this.signalDown( "onAddToRoot" );
			this.isRooted = true;
		}
		
		[slot] public function onRemoveFromRoot():void
		{
			globalPool.remove( this );
			this.signalDown( "onRemoveFromRoot" );
			this.isRooted = false;
		}
		
		public final function slot__getSigSlotInfo( children:Object, pipes:Object, slots:Object, signals:Object ):void
		{
			var c:Dictionary = this._getSigSlotChildren();
			var s:Dictionary = getSlots();
			
			for each( var child:SigSlotCollection in c )
			{
				var type:String = Object(child).constructor.toString();
				if( !(type in children) )
				{
					children[ type ] = [];
				}
				children[ type ].push( child );
			}
			
			for( var pipe:String in this.pipes )
			{
				pipes[ pipe ] = pipe;
			}
			
			for( var signal:String in this.signals )
			{
				signals[ signal ] = signal;
			}
			
			for( var slot:String in s )
			{
				if( slot.charAt(0) != "_" )
				{
					slots[slot] = slot;
				}
			}
		}
		
		public final function slot__onInitPipes( pipes:Dictionary ):void
		{
			if( this.pipes ) return;
			this.pipes = new Dictionary();
			for( var pipe:String in pipes )
			{
				this.pipes[ pipe ] = this[ pipes[pipe] ]; 
			}
		}
		
		protected function getSigSlotId():int
		{
			return this.id;
		}
		
		public function getSigSlotParent():SigSlot
		{
			return this.sigslotParent;
		}

		protected override function addChild( childC:SigSlotCollection ):void
		{
			var child:SigSlot = SigSlot(childC);
			super.addChild( childC );
			
			if( !child || child == this || child.sigslotParent == this ) return;

			if( child.sigslotParent )
			{
				child.sigslotParent.removeChild( child );
			}
						
			child.sigslotParent = this;
			
			if( !child.complete )
			{
				child.complete = true;
				child.signalDown("onParentConstructed");
			}
			
			child.message("onAddToParent");
			
			if( this.isRooted ) child.message("onAddToRoot");
		}
		
		protected override function removeChild( child:SigSlotCollection ):void
		{
			super.removeChild( child );
			if( !child || !(child is SigSlot) || (SigSlot(child).sigslotParent != this) ) return;
			
			child.message("onRemoveFromParent");
			if( this.isRooted ) child.message("onRemoveFromRoot");
			
			SigSlot(child).sigslotParent = null;
		}
		
		public function addPipe( name:String, func:Function ):void
		{
			if( !this.pipes ) this.pipes = new Dictionary();
			if( !(name in this.pipes) ) this.pipes[ name ] = func;
		}
		
		public function __getPipes():Dictionary
		{
			return this.pipes;
		}
		
		private static const SIG_CHILDREN:int = 1;
		private static const SIG_PARENT:int = 2;
		private static const SIG_RECURSIVE:int = 5;
		
		private function _emitSignal( name:String, sigtype:int, arguments:Array ):void
		{
			if( sigtype&SIG_PARENT )
			{
				if( this.sigslotParent && this.pipes && name in this.pipes )
				{
					if( this.pipes[name] ) this.pipes[name].apply( this, arguments );
					this.sigslotParent._emitSignal( name, SIG_PARENT+SIG_CHILDREN, arguments ); 
				}
				else
				{
					this.messageEx( name, arguments );
				}
			}
			else
			{
				if( isNaN(Number(Oboe.sigMiss[ name ])) ) Oboe.sigMiss[ name ] = 0;
				Oboe.sigMiss[ name ]++;
			}
			
			if( sigtype&SIG_CHILDREN )
			{
				if( (sigtype&SIG_RECURSIVE) == SIG_RECURSIVE )
				{
					var children:Dictionary = this._getSigSlotChildren();
					for each( var child:SigSlot in children )
					{
						if( name != "onTick" ) 
							trace( "hit " + getQualifiedClassName(child) + " with " + name );
						
						if( child.pipes && child.pipes[name] )
						{
							child.pipes[name].apply( child, arguments );
						}
						else
						{
							child.messageEx( name, arguments );
						}
						child._emitSignal( name, SIG_RECURSIVE, arguments );
					}
				}
				else 
				{
					super.signalDownEx( name, arguments )
				}
			}
		}
		
		protected function selfDestruct():void
		{
			if( !this.sigslotParent ) return;
			this.sigslotParent.removeChild( this );
		}
		
		public override function message( name:String, ... arguments ):void
		{
			super.messageEx( name, arguments );
			if( this.sigslotParent && this.pipes && name in this.pipes  )
			{
				this.pipes[name].apply( this, arguments );
				this.sigslotParent._emitSignal( name, SIG_PARENT+SIG_CHILDREN, arguments ); 
			}
		}
		
		protected override function signalDown(name:String, ...arguments):void
		{
			signals[ name ] = name;
			super.signalDownEx( name, arguments );
		}
		
		protected override function signalDownEx(name:String, arguments:Array):void
		{
			signals[ name ] = name;
			super.signalDownEx( name, arguments );
		}
		
		protected function signalUp( name:String, ... arguments ):void
		{
			signals[ name ] = name;
			if( this.sigslotParent )
			{
				this.sigslotParent._emitSignal( name, SIG_PARENT+SIG_CHILDREN, arguments ); 
			}
		}

		protected function shortSignal( name:String, ... arguments ):void
		{			
			signals[ name ] = name;
			if( this.sigslotParent )
			{
				this.sigslotParent._emitSignal( name, SIG_PARENT+SIG_CHILDREN, arguments ); 
				this.signalDownEx( name, arguments );
			}
			else
			{
				this.messageEx( name, arguments );
				this.signalDownEx( name, arguments );
			}
		}
		
		protected function signal( name:String, ... arguments ):void
		{
			signals[ name ] = name;
			if( this.sigslotParent )
			{
				this.sigslotParent._emitSignal( name, SIG_PARENT+SIG_RECURSIVE, arguments ); 
			}
			else
			{
				this._emitSignal( name, SIG_PARENT+SIG_RECURSIVE, arguments );
			}
		}
		
		protected function broadcastSignal( name:String, ...arguments ):void
		{
//			signals[ name ] = name;
//			var root:SigSlot = this;
//			while( root.sigslotParent )
//			{
//				root = root.sigslotParent;
//			}
//			
//			root._emitSignal( name, SIG_PARENT+SIG_RECURSIVE, arguments );
			globalPool.messageEx( name, arguments );
		}

		
		
		
		
		protected function _signalUp( name:String, ... arguments ):void
		{
			signals[ name ] = name;
			arguments.unshift( name );
			this.signalUp.apply( this, arguments );
		}
		
		protected function _signalDown( name:String, ... arguments ):void
		{
			signals[ name ] = name;
			arguments.unshift( name );
			this.signalDown.apply( this, arguments );
		}
		
		protected function _shortSignal( name:String, ... arguments ):void
		{
			signals[ name ] = name;
			arguments.unshift( name );
			this.shortSignal.apply( this, arguments );
		}
		
		protected function _signal( name:String, ... arguments ):void
		{
			signals[ name ] = name;
			arguments.unshift( name );
			this.signal.apply( this, arguments );
		}
		
		protected function _broadcastSignal( name:String, ...arguments ):void
		{
			signals[ name ] = name;
			arguments.unshift( name );
			this.broadcastSignal.apply( this, arguments );
		}
		
	}
}
